#include "sendsignal_b_study.h"
#include <math.h>
#include <float.h>

#include <stdio.h>
#include <iostream>

sendsignal_b_study::sendsignal_b_study() {
}

sendsignal_b_study::~sendsignal_b_study() {
}

void sendsignal_b_study::setstudysignalid(int id) {
  signalid = id;
}

//void sendsignal_b_study::setoutputsignalfromaxon(int signal_id) {
//  const int neuon_count_in_layer = getlayersize();
//  const int output_layer_id      = getlayercountmax();
//  int       start_neuron_index   = getstartneuronidforlayer(output_layer_id-1);
//  axon       axon_val;
//  for(int neuron_index = 0; neuron_index < neuon_count_in_layer; neuron_index ++)
//  {
//      p_neuron[neuron_index + start_neuron_index].setStudyBufferIndex(signal_id);
//      axon_val = p_neuron[neuron_index + start_neuron_index].getAxon();
//      axon_val.setBStudySignal(signal_id, axon_val.getFSignal());
//      p_neuron[neuron_index + start_neuron_index].setAxon(axon_val);
//  }
//}

void sendsignal_b_study::setexpectedsignal(ntype::signal_t* signal, int) {
  const int neuon_count_in_layer = getlayersize();
  const int output_layer_id      = getlayercountmax();
  int       start_neuron_index   = getstartneuronidforlayer(output_layer_id-1);
  for(int neuron_index = 0; neuron_index < neuon_count_in_layer; neuron_index ++)
  {
      p_neuron[neuron_index + start_neuron_index].setStudyBufferIndex(signalid);
      axon &axon_val = p_neuron[neuron_index + start_neuron_index].getAxon();
      axon_val.setBStudySignal(signalid, signal[neuron_index]);
      p_neuron[neuron_index + start_neuron_index].setAxon(axon_val);

      printf("neuron id[%d] study backward expected signal id[%d] = %f {backward from axon value = %f}\n",
                                                               neuron_index + start_neuron_index,
                                                               signalid,
                                                               signal[neuron_index],
                                                             p_neuron[neuron_index + start_neuron_index].getAxon().getBStudySignal(signalid));

  }
}

void sendsignal_b_study::send(double*, int) {
  const int neuon_count_in_layer = getlayersize();
  const int layers_count_max     = getlayercountmax();

  int start_neuron_index;
  int start_neuron_prev_layer_index;
  int neuron_index;
  int neuron_index_prev_layer;
  int cur_neuron_id;
  double val;
  axon   axon_val;
  synapse   synapse_val;

//  setoutputsignal();
  for(int layer_count = layers_count_max -1; layer_count >= 0; layer_count --)
  {
    start_neuron_index = getstartneuronidforlayer(layer_count);
    start_neuron_prev_layer_index = getstartneuronidforlayer(layer_count-1);

//printf("------ layer [%d] --------\n", layer_count);
/*вычислить обратный сигнал для нейронов текущего слоя*/
    for(neuron_index = 0; neuron_index < neuon_count_in_layer; neuron_index ++)
    {
      cur_neuron_id = neuron_index + start_neuron_index;
      p_neuron[cur_neuron_id].setStudyBufferIndex(signalid);

//printf("back axon signal: %f  neuron[%d]\n", p_neuron[cur_neuron_id].getAxon().getBStudySignal(signalid),
//                                               neuron_index + start_neuron_index);


      p_neuron[cur_neuron_id].calc_back_study();
#if 0
      for(ntype::synapsecount_t syn_count = 0; syn_count < p_neuron[cur_neuron_id].getSynapseCount(); syn_count ++)
      {
        val = p_neuron[cur_neuron_id].clac_back_synapse_w(syn_count);
        synapse_val = p_neuron[cur_neuron_id].getSynapse(syn_count);
        synapse_val.setWeightBack(signalid, val);
        p_neuron[cur_neuron_id].setSynapse(synapse_val, syn_count);

/*
        synapse synapse_print = p_neuron[cur_neuron_id].getSynapse(syn_count);

        printf("synapse_b_study val [neuronid = %d  synid = %d w = %f] = %f\n",
			                            cur_neuron_id,
			                            syn_count,
			                            synapse_print.getWeight(),
			                            synapse_print.getBSignalStudy(signalid));
*/
	  }
#endif
	}

/* вычислить и обновить аксоны предыдущего слоя */
    if(start_neuron_prev_layer_index >= 0)
    {
/* clear back axon value for prev layer */
      for(neuron_index_prev_layer = 0; neuron_index_prev_layer < neuon_count_in_layer; neuron_index_prev_layer ++) {
        axon_val = p_neuron[start_neuron_prev_layer_index + neuron_index_prev_layer].getAxon();
		axon_val.setBStudySignal(signalid, 0.0);
        p_neuron[start_neuron_prev_layer_index + neuron_index_prev_layer].setAxon(axon_val);
      }

/* вычислить сигнал для аксона предыдущего слоя */
      for(neuron_index = 0; neuron_index < neuon_count_in_layer; neuron_index ++)
      {
/*вычислить сигнал для аксона предыдущего слоя для определнного нейрона */
        val = 0.0;
        for(int index = 0; index < neuon_count_in_layer; index ++)
	    {
	      synapse_val = p_neuron[start_neuron_index + index].getSynapse(neuron_index);
		  val += synapse_val.getBSignalStudy(signalid);
        }
        val = val / neuon_count_in_layer;
        axon_val = p_neuron[start_neuron_prev_layer_index + neuron_index].getAxon();
		axon_val.setBStudySignal(signalid,val);
        p_neuron[start_neuron_prev_layer_index + neuron_index].setAxon(axon_val);
//        printf("back axon signal: %f  neuron[%d]\n", axon_val.getBStudySignal(signalid),
//                                                       neuron_index + start_neuron_prev_layer_index);
      }
    }
  }
}

  ntype::error_t sendsignal_b_study::geterror() {
    const int in_size      = layersize;
    const int layers_count = getlayercountmax();
    const int start_index  = getstartneuronidforlayer(layers_count -1);
    double    ret_error    = 0.;
    double    calc_val;
    double    insig;
    double    outsig;

    for(int icount = 0; icount < in_size; icount ++)
    {
      insig = p_neuron[0].getSynapse(icount).getBSignalStudy(signalid);
      outsig = p_neuron[start_index + icount].getAxon().getBStudySignal(signalid);
      calc_val = (outsig - insig) / insig;
      calc_val = fabs(calc_val);

//getLog()->print("err: -> %f  : in_sig -> %f : out_sig -> %f\n", ret_error, insig, outsig);
      ret_error += calc_val;
  }
  ret_error = ret_error / in_size;
  return ret_error < MIN_ERROR_VALUE ? 0.0 : ret_error;
}
